<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Calling BHF Services</title>
    <base href="http://bhs.googlecode.com/svn/trunk/"><!--[if IE]></base><![endif]-->
</head>
<body bgcolor="white">
<h1>Calling Blue Hole Framework (BHF) JSON Services From JavaScript</h1>
<p>
    If you are calling BHF Services from other client side frameworks, you will need to understand Raw Invocation
    and, if going cross domain with script elements, the callback parameters. If you will be using the BHF client side
    support at the JavaScript level, you can simply create a service and add the needed methods and concurrency control.
    If you do not mind using JSP, we also provide a service tag to do the service creation for you.
</p>
<h2>Raw Invocation</h2>
<p>
    BHF services accept an optional JSON argument as the 'a=' parameter of the invoking URL and returns a JSON result.
    For example, the invoking URL might look like
    <code>http://localhost:8080/example/service/stats/compute?a=%5B1,2,3,3,4,5%5D</code> (Notice that the opening and
    closing brackets have been URL encoded.) and return something like
    <code>{"histogram":{"1":1,"2":1,"3":2,"4":1,"5":1},"mean":3.0,"min":1,"max":5}</code>
    (Note that the URI used to invoke a Service method depends on your
    <a href='javadoc/org/bhf/providers/router/DefaultURLRouter.html'>routing strategy</a>;
    this example assumes default routing.) Thus, you can use just about any JavaScript library with BHF services or
    you can use the bhs.js library for convenience, as described below.
</p>
<h2>The <code>success</code> and <code>failure</code> Parameters</h2>
<p>
    When invoking services across domains (cross server, cross origin, ...), one cannot use the XMLHttpRequest
    invocation method used in bhs.js or other libraries. Instead one must resort to one of several hacks to work around
    the XMLHttpRequest same origin security policy. One or the more popular techniques employs dynamic script
    elements. To support this usage pattern, BHF services may be invoked with optional 'success=' and 'failure='
    parameters to wrap the returned JSON object with a function call. For example,
    <code>http://localhost:8080/example/service/stats/compute?a=%5B1,2,3,3,4,5%5D&callback=foo</code>
    would produce
    <code>foo({"histogram":{"1":1,"2":1,"3":2,"4":1,"5":1},"mean":3.0,"min":1,"max":5})</code>
</p>
<h2>Using bhs.js</h2>
<p>
    To use the bhs.js library, you will be using a BHF.Service object to make invocation requests to Services. Note
    that a Service object uses XmlHTTPRequest and, therefore, can only be used within a domain (same origin).
    Each Service enforces a dispatch policy on the requests managed by that Service:
</p>
    <dl>
        <dt><code><b>BHF.Service.DISPATCH_POLICY.CONCURRENT</b></code></dt><dd>Allow concurrent requests.</dd>
        <dt><code><b>BHF.Service.DISPATCH_POLICY.SERIAL</b></code></dt><dd>Only allow one request at a time (default).</dd>
    </dl>
<p>
    All policies govern ONLY the invocation requests associated with a single Service.
</p>
<p>
    To create a Service and add methods (MyApplication.stats, 'service/stats', and 'compute' are just for example)...
</p>
    <pre><code>
    MyApplication.stats = new BHF.Service( 'service/stats/{method}' );
    MyApplication.stats.addMethod( 'compute' );
    </code></pre>
<p>
    and to make an invocation request...
</p>
    <pre><code>
    MyApplication.stats.compute(
        {
            success: function( o )
            {
                ...
            },
            failure: function( o )
            {
                ...
            },
            precondition: function()
            {
                ...
            },
            rollback: function()
            {
                ...
            }
        },
        argument
    );
    </code></pre>
    <p>
        The invocation takes a callback object and an argument. The argument may be an object or a function. If an
        object, the object is marshalled and passed as the single argument to the service side method. If a function,
        the function is invoked and the result passed as the argument to the service side method. The function is
        not evaluated until the method is actually dispatched, which may be some time after it has been invoked when
        using a dispatch policy of BHF.Service.DISPATCH_POLICY.SERIAL.
    </p><p>
        If a precondition has been provided, it will be invoked when the method returns to check if the preconditions
        for the invocation still hold. The function returns <code>false</code> if they do not. If <code>true</code>
        then control passes to the success function. If <code>false</code> then the rollback function will be called.
        The most common rollback responses are to ignore the results or repeat the invocation with the current state.
        You can use the pre-defined functions <code>BHF.Service.ROLLBACK_POLICY.IGNORE</code> and
        <code>BHF.Service.ROLLBACK_POLICY.RETRY</code> for these options. If your situation is more complex, you
        can provide your own function to handle a rollback.
    </p><p>
        The most common precondition (for Services) is to check whether the argument passed to the method has changed.
        For this to work you must specify a function that returns the argument, rather than the argument itself, when
        making the call: so that the function may be invoked when the call returns and the value compared to the original
        value. Also, set the precondition callback to <code>BHF.Service.PRECONDITIONS.SAME_ARG</code>.
    </p>
    <dl>
        <dt><code>success</code></dt><dd>Call returned a 2xx code. o is the returned result.</dd>
        <dt><code>failure</code></dt><dd>Call returned a 4xx, or 5xx code. o is the thrown exception object.</dd>
        <dt><code>precondition</code></dt><dd>Return <code>true</code> if the preconditions for this invocation still hold.</dd>
        <dt><code>rollback</code></dt><dd>Precondition check failed.</dd>
    </dl>

<h2>Default Callbacks</h2>
<p>
    You can specify the default callback functions at the Service level: they may be overriden for each call. This
    technique is particularly useful for the <code>failure</code>, <code>precondition</code>,
    <code>rollback</code> callbacks.
</p>
    <pre><code>
    MyApplication.stats.setDefaultCallback(
        {
            success: function( o )
            {
                ...
            },
            failure: function( o )
            {
                ...
            },
            precondition: function()
            {
                ...
            },
            rollback: function()
            {
                ...
            }
        }
    );
    </code></pre>

<h2>Inline Synchronous Invocations</h2>
<p>
    You may also invoke a Service method synchronously much like you would a local method.
    <code>var result = MyApplication.stats.compute( getValues() );</code>
    Syncronous invocations have much to recommend because they avoid the state consistency problems of asynchronous
    invocation and much to detract because they lock down browser activity while the call is pending.
</p><p>
    Calling method can be ambiguous if you pass only a single argument. This could mean a synchronous invocation
    with one argument or an asynchronous invocation for no arguments, where the argument you supplied are the
    callbacks. BHF will check the argument to see if it looks like a callback object (specifically, checking
    for one of the callback methods) to try and disambiguate your intent; however, if you have no callbacks (perhaps
    you are using only the defaults set on the Service) this strategy will fail. In that case, you <em>must</em> provide
    a callback object defining at least one of the callbacks, even if that callback is not used or duplicates a
    default setting.
</p>
<h2>Handling Exceptions</h2>
<p>
    If the Service throws an exception, it will be propagated in one form or another to the client. As a general rule,
    the Service Module should catch at least all checked exceptions and convert them into a JSON representation using
    either <code>JSONExceptionProcessView</code> or <code>ErrorsProcessView</code> (or an equivalent) to ensure that
    the exception, as received by the client, is in a conventional form. You will receive this exception object in
    either your <code>failure</code> callback or directly from your service invocation, if invoked synchronously
    inline.
</p><p>
    You can handle an exception yourself or pass it off to <code>BHF.MessageStuffer</code>, which will try to
    find an HTML element, such as an input field, for each error message. (See BHF JavaScript documentation for
    details.)
    <code><pre>
        var messageHandler = BHF.MessageStuffer();
        var crud = &lt;bhf:service service="${page.crud}" /&gt;;
        var update = function( e )
        {
            messageHandler.clearPreviousMessages();
            wait.show();
            crud.update(
                {
                    success: function() {
                        wait.hide();
                    },
                    failure: function( e ) {
                        messageHandler.fieldException( e );
                        wait.hide();
                    }
                },
                BHF.Service.gatherFields( 'form' )
            );
        };
    </pre></code>
</p>
<h2>Using JSP</h2>
<p>
    If you choose to use JSPs, you can declare a service, equivalent to the code above, as follows. (Invoking the
    service is identical to the JavaScript example.)
</p>
<pre><code>
    &lt;%@ taglib uri="http://bhf.org/taglibs/bhf" prefix="bhs" %&gt;
    ...
    var myService = &lt;bhs:service /&gt;
</code></pre>
</body>
</html>